using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.XPath;
using System.Xml;
using System.Xml.Schema;
using System.Diagnostics;
using System.IO;
using Mpi.Common.Collections;
using Xeml.Document.Contracts;
using Xeml.Sampling;
using Mpi.Common;
using System.ComponentModel;
using System.Data.SqlClient;

namespace Xeml.Document
{
    /// <summary>
    /// The standard implementation of the IXemlDocument interface.
    /// </summary>
    public class XemlDocument : IDocument, INotifyPropertyChanged
    {
        public const string uri = "http://www.mpimp-golm.mpg.de/2009/XEML/XemlDocument";
        public const string xmlNameSpace = "http://www.mpimp-golm.mpg.de/2009/XEML";
        public const string XemlVersion = "1.1.0";

        #region Fields
        private bool isSchemaConform = true;
        private string name;
        private string xeml;
        private string _schemaLocation;

        private StoryBoard storyBoard;
        private DateTime startDate;
        private DocumentResources documentResources;
        private ProviderDataMappings _provDataMappings;
        private ExperimentHeader expHeader;
        #endregion

        #region Properties

        /// <summary>
        /// Contains general information about this experiment.
        /// </summary>
        public ExperimentHeader ExperimentHeader
        {
            get { return expHeader; }
            set { expHeader = value; }
        }

        /// <summary>
        /// A global unique id for the document.
        /// </summary>
        public Guid Id { get; private set; }

        /// <summary>
        /// The name of the experiment
        /// </summary>
        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                if (this.name != value)
                {
                    this.name = value;
                    OnChanged();
                    OnPropertyChanged("Name");
                }
            }
        }

        /// <summary>
        /// The source code of the document.
        /// </summary>
        public string Xeml
        {
            get
            {
                return xeml;
            }
        }

        /// <summary>
        /// Ressources of this document, like validation components and sample provider.
        /// </summary>
        public DocumentResources DocumentResources
        {
            get { return documentResources; }
        }

        /// <summary>
        /// A otional start date of the experiment
        /// </summary>
        public DateTime StartDate
        {
            get
            {
                return this.startDate;
            }
            set
            {
                if (this.startDate != value)
                {
                    this.startDate = value;
                    OnChanged();
                    OnPropertyChanged("StartDate");
                }
            }
        }

        /// <summary>
        /// The total time to observ.
        /// </summary>
        public TimeSpan ObservationTime
        {
            get { return this.storyBoard.ObservationTime; }
            set { this.storyBoard.ObservationTime = value; }
        }

        /// <summary>
        /// The design oft the experiment.
        /// </summary>
        public StoryBoard StoryBoard
        {
            get
            {
                return this.storyBoard;
            }
            set
            {
                if (this.storyBoard != value)
                {
                    this.storyBoard = value;
                }
            }
        }

        /// <summary>
        /// Mapping of observer id's to sampel id's
        /// </summary>
        public ProviderDataMappings ProviderDataMappings
        {
            get
            {
                return this._provDataMappings;
            }
        }

        #endregion

        #region IDocument methods
        #region constructors
        /// <summary>
        /// The standard constructor.
        /// </summary>
        public XemlDocument()
        {
            this.expHeader = new ExperimentHeader();
            this.StoryBoard = new StoryBoard(this);
            this.documentResources = new DocumentResources();
            this.documentResources.XemlDocument = this;
            this._provDataMappings = new ProviderDataMappings();

            this.NewId();
            this.name = "New experiment";
            this.storyBoard.ObservationTime = new TimeSpan(35, 0, 0, 0);
            this.startDate = DateTime.Now;

            this.storyBoard.Changed += new EventHandler(OnStoryBoardChanged);
            this.DocumentResources.Changed += new EventHandler(DocumentResources_Changed);
            this.ProviderDataMappings.CollectionChanged += new EventHandler(ProviderDataMappings_CollectionChanged);
            Story s = new Story();
            s.Label = "base line";
            this.storyBoard.Add(s);

            this.GenerateXeml();
        }

        public XemlDocument(string xemlCode)
            :this()
        {
            Load(xemlCode);
        }
        #endregion constructors

        void ProviderDataMappings_CollectionChanged(object sender, EventArgs e)
        {
            OnChanged();
        }

        void DocumentResources_Changed(object sender, EventArgs e)
        {
            OnChanged();
        }

        /// <summary>
        /// Creates a new id for this document.
        /// </summary>
        public void NewId()
        {
            this.Id = Guid.NewGuid();
        }

        /// <summary>
        /// Loads a document from a given path.
        /// </summary>
        /// <param name="path">The path to the document.</param>
        public void LoadFile(string @path)
        {
            using (StreamReader sr = new StreamReader(path, Encoding.Unicode))
            {
                Load(sr.ReadToEnd());
            }
        }

        /// <summary>
        /// Loads the document model from the given xeml source code
        /// </summary>
        /// <param name="xemlCode">The xeml source code.</param>
        public void Load(string xemlCode)
        {
            // create schema
            string filePath = Path.GetTempPath() + "XemlSchema.xsd";
            File.WriteAllText(filePath, Resource.XEMLSchema);
            this._schemaLocation = filePath;

            //isXmlWellformed = true;
            isSchemaConform = true;
            this.xeml = xemlCode;
            XmlDocument xpd = new XmlDocument();

            try
            {
                xpd.LoadXml(this.xeml);
            }
            catch (XmlException xEx)
            {
                OnValidation(SeverityCode.Error, "Invalid xml format. Loading not possible.: " + xEx.Message, this, true);
            }

            ValidationEventHandler validationHndl = new ValidationEventHandler(SchemaValidationHandler);
            xpd.Schemas.Add(xmlNameSpace, this._schemaLocation);
            xpd.Validate(validationHndl);

            if (!isSchemaConform)
            {
                OnValidation(SeverityCode.Warning, "The document is not schema conform. Try to load anyway.", this, false);
            }

            XPathNavigator xpn = xpd.CreateNavigator();
            Init(xpn);
            OnLoaded(); //report loaded
        }

        /// <summary>
        /// Validates the current document against the XEML schema.
        /// </summary>
        public void DoXmlValidation()
        {
            GenerateXeml();
            DoXmlValidation(this.xeml);
        }

        /// <summary>
        /// Validates a given XEML code against the XEML schema.
        /// </summary>
        /// <param name="xemlCode"></param>
        public void DoXmlValidation(string xemlCode)
        {
            string filePath = Path.GetTempPath() + "XemlSchema.xsd";
            File.WriteAllText(filePath, Resource.XEMLSchema);
            _schemaLocation = filePath;

            StringReader strtRe = new StringReader(xemlCode);


            XmlReaderSettings sett = new XmlReaderSettings();
            sett.Schemas.Add(xmlNameSpace, this._schemaLocation);
            sett.ValidationType = ValidationType.Schema; //disable validating 
            sett.ConformanceLevel = ConformanceLevel.Document;
            sett.ValidationEventHandler += new ValidationEventHandler(OnXmlValidationEventHandler);
            XmlReader r = XmlReader.Create(strtRe, sett);

            try
            {
                while (r.Read()) ;
            }
            catch (XmlException ex)
            {
                XmlValidationMessage vm = new XmlValidationMessage(SeverityCode.Error, "Invalid xml format: " + ex.Message, this, false, ex.LineNumber, ex.LinePosition);
                OnValidation(vm);
            }
        }


        /// <summary>
        /// Saves the xeml source code of thios document to a file.
        /// </summary>
        /// <param name="path">Tha path of the file.</param>
        /// <returns></returns>
        public void Save(string @path)
        {
            GenerateXeml();
            if (File.Exists(path))
                File.Delete(path);
            File.WriteAllText(path, this.xeml, Encoding.Unicode);
        }


        /// <summary>
        /// Generates the xeml code new.
        /// </summary>
        public void RefreshXeml()
        {
            GenerateXeml();
        }

        #endregion

        #region Xeml read

        private void Init(XPathNavigator xpn)
        {
            this.storyBoard.Changed -= new EventHandler(OnStoryBoardChanged);//undock changed events

            this.StoryBoard.Clear();
            this.ExperimentHeader.Keywords.Clear();
            this.ExperimentHeader.Experimenter.Clear();
            this.documentResources.Clear();

            xpn.MoveToRoot();
            xpn.MoveToChild("XEMLDocument", xmlNameSpace);

            XPathNavigator clone = xpn.Clone();
            if (clone.MoveToChild("Resources", xmlNameSpace))
            {
                InitResources(clone);
            }

            XPathNodeIterator it;

            clone = xpn.Clone();
            if (clone.MoveToChild("Data", xmlNameSpace))
            {
                InitData(clone);
            }

            xpn.MoveToChild("Experiment", xmlNameSpace);

            this.name = xpn.GetAttribute("Name", "");
            string x = xpn.GetAttribute("Id", "");
            try
            {
                this.Id = XmlConvert.ToGuid(xpn.GetAttribute("Id", ""));
            }
            catch (FormatException ex)
            {
                OnValidation(SeverityCode.Warning, ex.Message, this, false);
            }
            this.storyBoard.ObservationTime = TryTimespanSet(xpn.GetAttribute("ObservationTime", ""), this);
            string startDate = xpn.GetAttribute("StartDate", "");
            if (!string.IsNullOrEmpty(startDate))
            {
                this.StartDate = XmlConvert.ToDateTime(startDate, XmlDateTimeSerializationMode.Unspecified);
            }

            it = xpn.SelectChildren("Header", xmlNameSpace);
            if (it.MoveNext())
            {
                it = it.Current.SelectChildren("Experimenter", xmlNameSpace);
                if (it.MoveNext())
                {

                    this.expHeader.Experimenter.FirstName = XmlLexicalType.ConvertToToken(it.Current.GetAttribute("FirstName", ""));
                    this.expHeader.Experimenter.LastName = XmlLexicalType.ConvertToToken(it.Current.GetAttribute("LastName", ""));
                    this.expHeader.Experimenter.Email = it.Current.GetAttribute("Email", "");
                    this.expHeader.Experimenter.Organisation = it.Current.GetAttribute("Organisation", "");

                }

                it.Current.MoveToParent();
                it = it.Current.SelectChildren("Description", xmlNameSpace);

                if (it.MoveNext())
                    this.expHeader.Description = it.Current.Value;
                it.Current.MoveToParent();
                it = it.Current.SelectChildren("Keyword", xmlNameSpace);
                while (it.MoveNext())
                {
                    this.expHeader.Keywords.Add(new Keyword(it.Current.Value));
                }
            }

            // xpn.MoveToParent();

            it = xpn.SelectChildren("Story", xmlNameSpace);
            while (it.MoveNext())
            {
                InitStory(it.Current, null);
            }

            this.storyBoard.Changed += new EventHandler(OnStoryBoardChanged); //reassign change event
        }

        private void InitData(XPathNavigator xpn)
        {
            this.ProviderDataMappings.Clear();
            if (xpn.MoveToChild("SampleMapping", xmlNameSpace))
            {
                do
                {
                    int sampleId = Convert.ToInt32(xpn.GetAttribute("sample", ""));

                    if (xpn.MoveToChild("Sid", xmlNameSpace))
                    {
                        do
                        {
                            XPathNavigator clone = xpn.Clone();
                            string p = clone.GetAttribute("Provider", "");
                            SidMapping sid = null;

                            InitSampleMapping(out sid, sampleId, clone);
                            InitAnnotations(xpn.Clone(), sid);

                            if (!_provDataMappings.ContainsKey(p))
                            {
                                _provDataMappings.Add(p, new DataMappings());
                            }

                            if (_provDataMappings[p].Contains(sid))
                                OnValidation(SeverityCode.Warning, "Double usage of SidMapping", sid, false);
                            else
                                _provDataMappings[p].Add(sid, true);

                            //if (!_provDataMappings.ContainsKey(sid))
                            //{
                            //    if (_provDataMappings.Contains(sid))
                            //        OnValidation(SeverityCode.Warning, "Double usage of SidMapping", sid, false);

                            //    _provDataMappings.Add(sid);
                            //}
                            //else
                            //{
                            //    OnValidation(SeverityCode.Error, "Double entry in data mappings", sampleId, false);
                            //}
                        } while (xpn.MoveToNext("Sid", xmlNameSpace));
                        xpn.MoveToParent();
                    }

                } while (xpn.MoveToNext("SampleMapping", xmlNameSpace));
            }
        }

        private void InitSampleMapping(out SidMapping sid, int sampleId, XPathNavigator xpn)
        {
            sid = new SidMapping(sampleId);


            if (xpn.MoveToChild("Subkey", xmlNameSpace))
            {
                do
                {
                    string keyName = xpn.GetAttribute("Name", "");
                    string val = xpn.Value;
                    if (!sid.ForeignKeyMap.ContainsKey(keyName))
                    {

                        sid.ForeignKeyMap.Add(keyName, val);
                    }
                    else
                    {
                        OnValidation(SeverityCode.Error, "Double key entry in sid", sid, false);
                    }
                } while (xpn.MoveToNext("Subkey", xmlNameSpace));
            }

        }

        private void InitResources(XPathNavigator xpn)
        {
            XPathNodeIterator it;

            Dictionary<String, OntologyType> ontologyTypeOf = new Dictionary<string, OntologyType> { 
                { "EnvironmentalOntology", OntologyType.Environment },
                { "GenotypeOntology", OntologyType.Genotype },
                { "OrganismStructureOntology", OntologyType.OrganismStructure },
                { "DevelopmentalOntology", OntologyType.Developmental },
                { "PositioningOntology", OntologyType.Positioning },
            };

            Dictionary<string, Dictionary<string, string>> resources = new Dictionary<string, Dictionary<string, string>>();
            foreach (KeyValuePair<String, OntologyType> item in ontologyTypeOf)
            {
                String name = item.Key;
                OntologyType type = item.Value;

                // gather all the information about the ontologies, reject the ones that are defined multiple times
                it = xpn.SelectChildren(name, xmlNameSpace);
                while (it.MoveNext())
                {
                    string alias = it.Current.GetAttribute("NS", "");
                    string location = it.Current.GetAttribute("InstanceLocation", "");
                    string uri = it.Current.GetAttribute("HandlerUri", "");

                    if (resources.ContainsKey(uri))
                    {
                        OnValidation(SeverityCode.Error, String.Concat(name, " double entry. Resource was rejected!"), alias, false);
                    }
                    else
                    {
                        Dictionary<string, string> alias_location = new Dictionary<string, string>() {
                            { "alias", alias },
                            { "location", location }
                        };
                        resources.Add(uri, alias_location);
                    }
                }
            }

            foreach (var resource in resources)
            {
                String uri = resource.Key;

                this.documentResources.Add(uri, resources[uri]["alias"], resources[uri]["location"], false);
            }

            it = xpn.SelectChildren("DataProvider", xmlNameSpace);
            while (it.MoveNext())
            {
                DataProviderResource spr = new DataProviderResource();
                spr.FriendlyName = it.Current.GetAttribute("Name", "");
                //spr.ConnectionString = it.Current.GetAttribute("ConnectionString", "");
                String ConfigurationName = it.Current.GetAttribute("ConfigurationName", "");
                if (ConfigurationName != String.Empty)
                {
                    spr.LoadConfiguredComponent(ConfigurationName);
                }
                else
                {
                    spr.LoadComponent(it.Current.GetAttribute("ProviderUri", ""));
                }
                if ((this.documentResources.DataProvider.Find(spr.FriendlyName) == null))
                {
                    this.documentResources.DataProvider.Add(spr);
                }
                else
                {
                    OnValidation(SeverityCode.Error, "DataProviderResource double entry. Resource was rejected!", spr, false);
                }
            }
        }

        /// <summary>
        /// Reads a story and all substories.
        /// </summary>
        /// <param name="xpn">The xml path navigator.</param>
        /// <param name="node">The parent node within the storyboard.</param>
        private void InitStory(XPathNavigator xpn, TreeNode<StoryBase> node)
        {
            XPathNodeIterator it;
            TreeNode<StoryBase> tns = null;// = node;
            if (xpn.LocalName == "Story")
            {
                Story s = new Story();
                InitAnnotations(xpn.Clone(), s);

                s.Label = xpn.GetAttribute("Label", "");
                tns = new TreeNode<StoryBase>(s);

                this.StoryBoard.Add(tns);
                OnValidation(SeverityCode.Message, "Story found", s, false);

                it = xpn.SelectChildren("IndividualsPool", xmlNameSpace);
                while (it.MoveNext())
                {
                    // InitIndividuals(it.Current, s);
                    InitPools(it.Current, s);
                }
            }
            else if (xpn.LocalName == "StorySplit")
            {
                StorySplit s = new StorySplit();
                InitAnnotations(xpn.Clone(), s);
                s.Label = xpn.GetAttribute("Label", "");
                s.TimePoint = TryTimespanSet(xpn.GetAttribute("TimePoint", ""), s);
                tns = new TreeNode<StoryBase>(s);
                this.StoryBoard.Add(node, tns);

                OnValidation(SeverityCode.Message, "StorySplit found", s, false);
            }



            it = xpn.SelectChildren("Variable", xmlNameSpace);
            while (it.MoveNext())
            {
                InitVariable(it.Current.Clone(), tns.Content);
            }

            it = xpn.SelectChildren("ObservationPoint", xmlNameSpace);
            while (it.MoveNext())
            {
                InitObserverPoint(it.Current.Clone(), tns.Content);

            }

            it = xpn.SelectChildren("Sample", xmlNameSpace);
            while (it.MoveNext())
            {
                InitSample(it.Current.Clone(), tns.Content);
            }

            it = xpn.SelectChildren("Event", xmlNameSpace);
            while (it.MoveNext())
            {
                InitEvent(it.Current.Clone(), tns.Content);
            }

            it = xpn.SelectChildren("StorySplit", xmlNameSpace);
            while (it.MoveNext())
            {
                InitStory(it.Current.Clone(), tns);
            }
            return;
        }

        private void InitEvent(XPathNavigator xpn, StoryBase storyBase)
        {
            Event e = new Event();
            InitAnnotations(xpn.Clone(), e);
            TimeSpan t = TryTimespanSet(xpn.GetAttribute("TimePoint", ""), e);
            e.TimePoint = t;
            e.Label = xpn.GetAttribute("Label", "");
            storyBase.Events.Add(e);
        }

        private void InitSample(XPathNavigator xPathNavigator, StoryBase storyBase)
        {
            Sample s = new Sample();
            InitAnnotations(xPathNavigator.Clone(), s);
            xPathNavigator.MoveToAttribute("Id", "");
            s.Id = xPathNavigator.ValueAsInt;
            xPathNavigator.MoveToParent();
            string intermix = xPathNavigator.GetAttribute("TimeIntermix", "");
            switch (intermix)
            {
                case "Min":
                    {
                        goto default;
                    }
                case "Max":
                    {
                        s.TimeIntermix = TimeIntermix.Max;
                        break;
                    }
                case "Mean":
                    {
                        s.TimeIntermix = TimeIntermix.Average;
                        break;
                    }
                default:
                    {
                        s.TimeIntermix = TimeIntermix.Min;
                        break;
                    }
            }

            XPathNodeIterator it = xPathNavigator.SelectChildren("BioSource", xmlNameSpace);

            while (it.MoveNext())
            {

                bool found = false;
                it.Current.MoveToAttribute("partition", "");
                int partId = it.Current.ValueAsInt;
                it.Current.MoveToParent();

                foreach (ObservationPoint op in storyBase.ObservationPoints)
                {
                    foreach (Observation ob in op.Observations)
                    {
                        foreach (Partition part in ob.Partitions)
                        {
                            if (part.Id == partId)
                            {
                                s.AddPartition(part);
                                found = true;
                                break;
                            }
                        }
                        if (found)
                            break;
                    }
                    if (found)
                        break;
                }
            }

            storyBase.Samples.Add(s);

        }

        private void InitPools(XPathNavigator xpn, Story s)
        {
            IndividualsPool ip = new IndividualsPool();
            InitAnnotations(xpn.Clone(), ip);

            ip.NameSpace = xpn.GetAttribute("NS", "");
            ip.GermPlasm = xpn.GetAttribute("GermPlasm", "");
            XPathNodeIterator it;



            it = xpn.SelectChildren("Individual", xmlNameSpace);
            while (it.MoveNext())
            {
                Individual ind = new Individual();
                InitAnnotations(it.Current.Clone(), ind);
                ind.Id = XmlConvert.ToInt32(it.Current.GetAttribute("Id", ""));
                ip.Individuals.Add(ind);
            }
            s.Pools.Add(ip);
        }

        private void InitObserverPoint(XPathNavigator xpn, StoryBase storyBase)
        {
            ValidationMessage vm;
            ObservationPoint op = new ObservationPoint();
            InitAnnotations(xpn.Clone(), op);
            op.Id = Convert.ToInt32(xpn.GetAttribute("Id", ""));
            op.TimePoint = TryTimespanSet(xpn.GetAttribute("TargetTime", ""), op);

            XPathNodeIterator it = xpn.SelectChildren("Observation", xmlNameSpace);
            while (it.MoveNext())
            {
                Observation ob = new Observation();

                InitAnnotations(it.Current.Clone(), ob);
                ob.Destructive = false;
                ob.Duration = TryTimespanSet(it.Current.GetAttribute("Duration", ""), ob);

                if (it.Current.MoveToAttribute("IndividualInfluence", ""))
                {
                    ob.IndividualInfluence = it.Current.ValueAsBoolean;
                    it.Current.MoveToParent();
                }
                //ob.IndividualInfluence = Convert.ToBoolean(it.Current.GetAttribute("IndividualInfluence", ""));


                int indId = -1;
                string indIDStr = it.Current.GetAttribute("individual", "");
                if (!String.IsNullOrEmpty(indIDStr))
                    indId = Convert.ToInt32(indIDStr);

                string nsStr = xpn.GetAttribute("NS", "");
                string germPlasmStr = xpn.GetAttribute("GermPlasm", "");
                string fqName;
                if (!String.IsNullOrEmpty(nsStr) && nsStr != "none")
                    fqName = String.Format("{0}:{1}", nsStr, germPlasmStr);
                else
                    fqName = germPlasmStr;

                TreeNode<StoryBase> node = storyBase.StoryBoard.FindNode(storyBase);
                while (node.Level > 0) // find the root story
                {
                    node = node.Parent;
                }


                Individual ind;
                IndividualsPool ip = (node.Content as Story).Pools.Find(fqName);
                if (ip != null)
                {
                    ob.IndividualsPool = ip;
                    ind = ip.Individuals.Find(indId);
                    if (ind != null)
                        ob.Individual = ind;
                    else if (indId >= 0)
                        OnValidation(SeverityCode.Error, "Individual not found", ob, false);
                }
                else
                    OnValidation(SeverityCode.Error, "IndividualsPool not found.", ob, false);



                //hello

                InitPartitions(it.Current.Clone(), ob);
                InitDevelopmentalStage(it.Current.Clone(), ob);
                op.Observations.Add(ob);
            }

            it = xpn.SelectChildren("DestructiveObservation", xmlNameSpace);
            while (it.MoveNext())
            {
                Observation ob = new Observation();
                InitAnnotations(it.Current.Clone(), ob);
                ob.Destructive = true;
                ob.Duration = TryTimespanSet(it.Current.GetAttribute("Duration", ""), ob);
                ob.IndividualInfluence = true;


                int indId = -1;
                string indIDStr = it.Current.GetAttribute("individual", "");
                if (!String.IsNullOrEmpty(indIDStr))
                    indId = Convert.ToInt32(indIDStr);

                string nsStr = it.Current.GetAttribute("poolNS", "");
                string germPlasmStr = it.Current.GetAttribute("poolGermPlasm", "");
                string fqName;
                if (!String.IsNullOrEmpty(nsStr) && nsStr != "none")
                    fqName = String.Format("{0}:{1}", nsStr, germPlasmStr);
                else
                    fqName = germPlasmStr;


                TreeNode<StoryBase> node = storyBase.StoryBoard.FindNode(storyBase);
                while (node.Level > 0) // find the root story
                {
                    node = node.Parent;
                }


                Individual ind;
                IndividualsPool ip = (node.Content as Story).Pools.Find(fqName);
                if (ip != null)
                {
                    ob.IndividualsPool = ip;
                    ind = ip.Individuals.Find(indId);
                    if (ind != null)
                        ob.Individual = ind;
                    else if (indId >= 0)
                        OnValidation(SeverityCode.Error, "Individual not found", ob, false);
                }
                else
                    OnValidation(SeverityCode.Error, "IndividualsPool not found.", ob, false);



                InitPartitions(it.Current.Clone(), ob);
                InitDevelopmentalStage(it.Current.Clone(), ob);
                op.Observations.Add(ob);
            }

            //string duration = xpn.GetAttribute("Duration", "");
            //if(!string.IsNullOrEmpty(duration))
            //    op.Duration = TryTimespanSet(xpn.GetAttribute("Duration", ""), op);

            if (storyBase.ObservationPoints.Contains(op.TimePoint))
            {
                vm = new ValidationMessage(SeverityCode.Error, "ObservationPoint double entry. ObservationPoint was rejected!", op, false);
                OnValidation(vm);
            }
            else
            {
                storyBase.ObservationPoints.Add(op);
                vm = new ValidationMessage(SeverityCode.Message, "ObservationPoint found", op, false);
                OnValidation(vm);
            }
        }

        private void InitDevelopmentalStage(XPathNavigator xpn, Observation o)
        {
            XPathNodeIterator it;
            it = xpn.SelectChildren("DevelopmentalStage", xmlNameSpace);
            while (it.MoveNext())
            {
                string nsAlias = it.Current.GetAttribute("NS", "");
                string id = it.Current.GetAttribute("TermId", "");
                string term = it.Current.GetAttribute("term", "");
                string val = it.Current.Value;

                Term ds = new Term(id);
                ds.NameSpaceAlias = nsAlias;
                ds.Name = term;


                if (!o.DevelopmentalStages.Contains(id))
                {
                    o.DevelopmentalStages.Add(ds);
                    InitAnnotations(it.Current.Clone(), ds);
                }
                else
                    OnValidation(SeverityCode.Error, "Double annotation tag entry. Skip annotation!", o, false);
            }
        }

        private void InitPartitions(XPathNavigator xPathNavigator, Observation ob)
        {
            XPathNodeIterator it = xPathNavigator.SelectChildren("Partition", xmlNameSpace);
            while (it.MoveNext())
            {
                Partition part = new Partition();
                InitAnnotations(it.Current.Clone(), part);
                part.Id = Convert.ToInt32(it.Current.GetAttribute("Id", ""));

                if (it.Current.MoveToChild("Annotation", xmlNameSpace))
                {
                    //part.Annotation = it.Current.Values;
                    it.Current.MoveToParent();
                }
                XPathNodeIterator it2 = it.Current.SelectChildren("Material", xmlNameSpace);
                while (it2.MoveNext())
                {
                    string id = it2.Current.GetAttribute("TermId", "");
                    Term mat = new Term(id);
                    InitAnnotations(it2.Current.Clone(), mat);
                    mat.NameSpaceAlias = it2.Current.GetAttribute("NS", "");
                    mat.Name = it2.Current.GetAttribute("term", "");
                    part.Material.Add(mat);
                }
                XPathNodeIterator itPos = it.Current.SelectChildren("Position", xmlNameSpace);
                while (itPos.MoveNext())
                {
                    string id = it2.Current.GetAttribute("TermId", "");
                    VariableTerm pos = new VariableTerm(id);
                    InitAnnotations(itPos.Current.Clone(), pos);
                    pos.NameSpaceAlias = itPos.Current.GetAttribute("NS", "");
                    pos.Name = itPos.Current.GetAttribute("term", "");
                    part.Positions.Add(pos);
                    XPathNodeIterator itPosVals = itPos.Current.SelectChildren("Value", xmlNameSpace);
                    while (itPosVals.MoveNext())
                    {
                        Value v = new Value();
                        v.Context = itPosVals.Current.GetAttribute("Context", "");
                        v.Unit = itPosVals.Current.GetAttribute("Unit", "");
                        v.Label = itPosVals.Current.GetAttribute("Label", "");
                        v.Val = itPosVals.Current.Value;
                        pos.Values.Add(v);
                    }
                }
                if (!ob.Partitions.Contains(part.Id))
                    ob.Partitions.Add(part);
                else
                    OnValidation(SeverityCode.Error, "Double entry for partition. Partition will be skipped.", part, false);
            }
        }

        private void InitVariable(XPathNavigator xpn, StoryBase st)
        {
            ValidationMessage vm;
            string termId = xpn.GetAttribute("TermId", "");
            DynamicTerm p = new DynamicTerm(termId);
            InitAnnotations(xpn.Clone(), p);

            p.Name = XmlLexicalType.ConvertToToken(xpn.GetAttribute("Name", ""));
            p.NameSpaceAlias = xpn.GetAttribute("NS", "");


            //Ontology is not loaded yet.

            //OntologyHandlerResource ohr = this.documentResources.EnvironmentOntologyHandler.Find(p.NameSpaceAlias);
            //if (ohr != null)
            //{
            //    p.NameSpace = ohr.Handler.NameSpace;
            //}
            //else
            //    OnValidation(SeverityCode.Error, "Namespace not found", p.NameSpaceAlias, false);

            // p.GroupPath = XmlLexicalType.ConvertToToken(xpn.GetAttribute("GroupPath", ""));
            //p.Unit = xpn.GetAttribute("Unit", "");
            if (p.NameSpaceAlias != "none" && !this.documentResources.Contains(p.NameSpaceAlias, OntologyType.Environment))
            {
                vm = new ValidationMessage(SeverityCode.Warning, "Namespace resource not found!", p, false);
                OnValidation(vm);
            }
            if (st.Variables.Contains(p.TermId))
            {
                vm = new ValidationMessage(SeverityCode.Error, "Variable double entry. Variable was rejected", p, false);
                OnValidation(vm);
            }
            else
            {
                st.Variables.Add(p);
                vm = new ValidationMessage(SeverityCode.Message, "Variable added.", p, false);
                OnValidation(vm);
            }

            XPathNodeIterator xpi = xpn.SelectChildren("ValueSet", xmlNameSpace);
            while (xpi.MoveNext())
            {
                TimeSpan ts = TryTimespanSet(xpi.Current.GetAttribute("TimePoint", ""), p);
                XPathNavigator clone = xpi.Current.Clone();
                if (clone.MoveToFirstChild())
                {
                    InitValues(clone, p.Values, ts, st);
                }

            }
        }

        private void InitValues(XPathNavigator xpn, DynamicValueCollection vals, TimeSpan timePoint, StoryBase sb)
        {
            if (xpn.LocalName == "Value")
            {
                DynamicValue v = new DynamicValue();
                v.IsCycleValue = false;
                v.Time = timePoint;
                v.Context = xpn.GetAttribute("Context", "");
                v.Unit = xpn.GetAttribute("Unit", "");
                v.Label = xpn.GetAttribute("Label", "");
                v.Val = xpn.Value;

                DynamicValueBase toFind = vals.Find(delegate(DynamicValueBase vb) { return vb.Time == timePoint && vb.Context == v.Context; });
                if (toFind != null)
                {
                    vals.Remove(toFind);
                    OnValidation(SeverityCode.Warning, "Value double entry. The value will be overriden", v, false);
                }
                if (sb is StorySplit)
                {
                    if (v.Time >= (sb as StorySplit).TimePoint)
                    {
                        vals.Add(v);
                        OnValidation(SeverityCode.Message, "Values added.", v, false);
                    }
                    else
                        OnValidation(SeverityCode.Warning, "Values time is lesser than the split time. Values rejected.", v, false);
                }
                else
                {
                    vals.Add(v);
                    OnValidation(SeverityCode.Message, "Values added.", v, false);
                }
            }
            else if (xpn.LocalName == "Cycle")
            {

                ValidationMessage vm;
                Cycle c = new Cycle();
                c.Time = timePoint;
                c.Context = xpn.GetAttribute("Context", "");
                c.Unit = xpn.GetAttribute("Unit", "");
                c.Label = xpn.GetAttribute("Label", "");
                XPathNavigator valNav = xpn.Clone();
                if (valNav.MoveToFirstChild())
                {
                    do
                    {
                        DynamicValue v = new DynamicValue();
                        v.IsCycleValue = true;
                        v.Time = TryTimespanSet(valNav.GetAttribute("Duration", ""), v);
                        v.Val = valNav.Value;
                        c.Values.Add(v);
                        vm = new ValidationMessage(SeverityCode.Message, "Cycle value added.", v, false);
                        OnValidation(vm);

                    } while (valNav.MoveToNext());
                }
                DynamicValueBase toFind = vals.Find(delegate(DynamicValueBase vb) { return vb.Time == timePoint && vb.Context == c.Context; });
                if (toFind != null)
                {
                    vals.Remove(toFind);
                    OnValidation(SeverityCode.Warning, "Cycle double entry. The cycle will be overriden!", c, false);
                }

                if (sb is StorySplit)
                {
                    if (c.Time >= (sb as StorySplit).TimePoint)
                    {
                        vals.Add(c);
                        OnValidation(SeverityCode.Message, "Cycle added.", c, false);
                    }
                    else
                        OnValidation(SeverityCode.Warning, "Cycle time is lesser than the split time. Values rejected.", c, false);
                }
                else
                {
                    vals.Add(c);
                    OnValidation(SeverityCode.Message, "Cycle added.", c, false);
                }
            }
            if (xpn.MoveToNext())
            {
                InitValues(xpn, vals, timePoint, sb);
            }
            else
                return;
        }

        private void InitAnnotations(XPathNavigator xpn, XemlAnnotatableObject xo)
        {
            XPathNodeIterator it;
            it = xpn.SelectChildren("Annotation", xmlNameSpace);
            while (it.MoveNext())
            {
                string tag = it.Current.GetAttribute("Tag", "");
                string val = it.Current.Value;
                if (!xo.Annotations.Contains(tag))
                {
                    xo.Annotations.Add(new TaggedAnnotation(tag, val));
                }
                else
                    OnValidation(SeverityCode.Error, "Double annotation tag entry. Skip annotation!", xo, false);
            }
        }
        #endregion

        #region Purge
        /// <summary>
        /// this method is intended to purge all information within a document except the structure
        /// </summary>
        public void Purge()
        {
            foreach (TreeNode<StoryBase> node in this.storyBoard.VirtualRoot.SubNodes(false))
            {
                PurgeStories(node);
            }
        }

        private void PurgeStories(TreeNode<StoryBase> node)
        {
            StoryBase story = node.Content;
            if (story is Story)
            {
                PurgeIndividuals(story as Story);
            }

            foreach (DynamicTerm p in story.Variables)
            {
                PurgeVariable(p);
            }
            story.Variables.Clear();
            story.Variables.TrimExcess();

            foreach (ObservationPoint op in story.ObservationPoints)
            {
                PurgeObservationPoint(op);
            }

            story.Samples.Clear();
            story.Samples.TrimExcess();

            foreach (TreeNode<StoryBase> subNode in node.SubNodes(false))
            {
                PurgeStories(subNode);
            }
        }

        private void PurgeObservationPoint(ObservationPoint op)
        {
            foreach (Observation ob in op.Observations)
            {
                ob.IndividualsPool.Individuals.Clear();
                ob.IndividualsPool.Individuals.TrimExcess();

                foreach (Partition part in ob.Partitions)
                {
                    part.Material.Clear();
                    part.Material.TrimExcess();

                    foreach (Term pos in part.Positions)
                    {
                        ((VariableTerm)pos).Values.Clear();
                        ((VariableTerm)pos).Values.TrimExcess();
                    }
                }
                ob.Partitions.Clear();
                ob.Partitions.TrimExcess();

                ob.DevelopmentalStages.Clear();
                ob.DevelopmentalStages.TrimExcess();
            }
            op.Observations.Clear();
            op.Observations.TrimExcess();
        }

        private void PurgeVariable(DynamicTerm p)
        {
            p.Values.Clear();
            p.Values.TrimExcess();
        }

        private void PurgeIndividuals(Story story)
        {
            foreach (IndividualsPool ip in story.Pools)
            {
                ip.Individuals.Clear();
                ip.Individuals.TrimExcess();
            }
        }

        #endregion Purge

        #region Xeml write
        private void GenerateXeml()
        {
            StringBuilder sb = new StringBuilder();
            XmlWriterSettings xws = new XmlWriterSettings();
            xws.IndentChars = "\t";
            xws.Indent = true;
            xws.ConformanceLevel = ConformanceLevel.Document;
            xws.Encoding = Encoding.Unicode;
            xws.OmitXmlDeclaration = false;

            using (XmlWriter xw = XmlWriter.Create(sb, xws))
            {
                xw.WriteStartDocument();
                xw.WriteStartElement("xeml", "XEMLDocument", xmlNameSpace);
                xw.WriteAttributeString("XemlVersion", XemlVersion);
                WriteResources(xw);
                xw.WriteStartElement("xeml", "Experiment", xmlNameSpace);
                xw.WriteAttributeString("Id", "{" + XmlConvert.ToString(this.Id) + "}");
                xw.WriteAttributeString("Name", this.Name);

                xw.WriteAttributeString("ObservationTime", this.storyBoard.ObservationTime.ToString());
                xw.WriteAttributeString("StartDate", XmlConvert.ToString(this.startDate, XmlDateTimeSerializationMode.Unspecified));
                WriteHeader(xw);

                foreach (TreeNode<StoryBase> node in this.storyBoard.VirtualRoot.SubNodes(false))
                {
                    WriteStories(xw, node);
                }
                xw.WriteEndElement();//end of experiment

                if (this.ProviderDataMappings.Count > 0)
                {
                    xw.WriteStartElement("xeml", "Data", xmlNameSpace);
                    foreach (var providerName in _provDataMappings.Keys)
                    {
                        foreach (int sampleId in _provDataMappings[providerName].ListDistinctIdentifiers())
                        {
                            WriteSampleMapping(xw, sampleId, providerName);
                        }
                    }

                    xw.WriteEndElement();
                }

                xw.WriteEndElement();
                xw.WriteEndDocument();
                xw.Close();
            }

            this.xeml = sb.ToString();
        }

        private void WriteHeader(XmlWriter xw)
        {
            xw.WriteStartElement("xeml", "Header", xmlNameSpace);

            if (!expHeader.Experimenter.IsEmpty())
            {
                xw.WriteStartElement("xeml", "Experimenter", xmlNameSpace);
                xw.WriteAttributeString("FirstName", expHeader.Experimenter.FirstName);
                xw.WriteAttributeString("LastName", expHeader.Experimenter.LastName);
                xw.WriteAttributeString("Email", expHeader.Experimenter.Email);
                xw.WriteAttributeString("Organisation", expHeader.Experimenter.Organisation);
                xw.WriteEndElement();
            }


            xw.WriteStartElement("xeml", "Description", xmlNameSpace);
            if (!String.IsNullOrEmpty(this.ExperimentHeader.Description))
            {
                xw.WriteValue(this.ExperimentHeader.Description);

            }
            xw.WriteEndElement();

            foreach (Keyword k in this.ExperimentHeader.Keywords)
            {
                xw.WriteStartElement("xeml", "Keyword", xmlNameSpace);
                xw.WriteValue(XmlLexicalType.ConvertToToken(k.Value));
                xw.WriteEndElement();
            }

            xw.WriteEndElement();
        }

        private void WriteResources(XmlWriter xw)
        {
            xw.WriteStartElement("xeml", "Resources", xmlNameSpace);

            foreach (OntologyHandlerResource ohr in this.documentResources.EnvironmentOntologyHandler)
            {
                xw.WriteStartElement("xeml", "EnvironmentalOntology", xmlNameSpace);
                xw.WriteAttributeString("NS", ohr.NameSpaceAlias);
                xw.WriteAttributeString("InstanceLocation", ohr.Location);
                xw.WriteAttributeString("HandlerUri", ohr.Uri);
                xw.WriteEndElement();
            }

            foreach (OntologyHandlerResource ohr in this.documentResources.GermPlasmOntologyHandler)
            {
                xw.WriteStartElement("xeml", "GenotypeOntology", xmlNameSpace);
                xw.WriteAttributeString("NS", ohr.NameSpaceAlias);
                xw.WriteAttributeString("InstanceLocation", ohr.Location);
                xw.WriteAttributeString("HandlerUri", ohr.Uri);
                xw.WriteEndElement();
            }

            foreach (OntologyHandlerResource ohr in this.documentResources.OrganismStructureOntologyHandler)
            {
                xw.WriteStartElement("xeml", "OrganismStructureOntology", xmlNameSpace);
                xw.WriteAttributeString("NS", ohr.NameSpaceAlias);
                xw.WriteAttributeString("InstanceLocation", ohr.Location);
                xw.WriteAttributeString("HandlerUri", ohr.Uri);
                xw.WriteEndElement();
            }

            foreach (OntologyHandlerResource ohr in this.documentResources.DevelopmentalOntologyHandler)
            {
                xw.WriteStartElement("xeml", "DevelopmentalOntology", xmlNameSpace);
                xw.WriteAttributeString("NS", ohr.NameSpaceAlias);
                xw.WriteAttributeString("InstanceLocation", ohr.Location);
                xw.WriteAttributeString("HandlerUri", ohr.Uri);
                xw.WriteEndElement();
            }

            foreach (OntologyHandlerResource ohr in this.documentResources.PositioningOntologyHandler)
            {
                xw.WriteStartElement("xeml", "PositioningOntology", xmlNameSpace);
                xw.WriteAttributeString("NS", ohr.NameSpaceAlias);
                xw.WriteAttributeString("InstanceLocation", ohr.Location);
                xw.WriteAttributeString("HandlerUri", ohr.Uri);
                xw.WriteEndElement();
            }

            foreach (DataProviderResource spr in this.documentResources.DataProvider)
            {
                xw.WriteStartElement("xeml", "DataProvider", xmlNameSpace);
                xw.WriteAttributeString("Name", spr.FriendlyName);
                //  xw.WriteAttributeString("ConnectionString", XmlLexicalType.ConvertToToken(spr.ConnectionString));
                xw.WriteAttributeString("ProviderUri", spr.Uri);
                if (spr.Provider.Configuration != null) // only save the config if we actually have one
                {
                    xw.WriteAttributeString("ConfigurationName", spr.Provider.Configuration.Name);
                }
                xw.WriteEndElement();
            }
            xw.WriteEndElement();
        }

        private void WriteStories(XmlWriter xw, TreeNode<StoryBase> node)
        {
            StoryBase story = node.Content;
            if (story is Story)
            {
                xw.WriteStartElement("xeml", "Story", xmlNameSpace);
                xw.WriteAttributeString("Label", XmlLexicalType.ConvertToToken((story as Story).Label));
                WriteAnnotations(xw, story);
                WriteIndividuals(xw, story as Story);

            }
            else if (story is StorySplit)
            {
                xw.WriteStartElement("xeml", "StorySplit", xmlNameSpace);
                xw.WriteAttributeString("Label", XmlLexicalType.ConvertToToken((story as StorySplit).Label));
                xw.WriteAttributeString("TimePoint", (story as StorySplit).TimePoint.ToString());
                WriteAnnotations(xw, story);
            }

            foreach (DynamicTerm p in story.Variables)
            {
                WriteParameter(xw, p);
            }

            foreach (ObservationPoint op in story.ObservationPoints)
            {
                WriteObservationPoint(xw, op);
            }

            foreach (Sample s in story.Samples)
            {
                WriteSamples(xw, s);
            }

            foreach (Event ev in story.Events)
            {
                WriteEvent(xw, ev);
            }

            foreach (TreeNode<StoryBase> subNode in node.SubNodes(false))
            {
                WriteStories(xw, subNode);
            }
            xw.WriteEndElement();
        }

        private void WriteSamples(XmlWriter xw, Sample s)
        {
            xw.WriteStartElement("xeml", "Sample", xmlNameSpace);
            xw.WriteAttributeString("Id", XmlConvert.ToString(s.Id));
            xw.WriteAttributeString("TimeIntermix", s.TimeIntermix.ToString());
            WriteAnnotations(xw, s);

            foreach (Partition part in s.Partitions)
            {
                xw.WriteStartElement("xeml", "BioSource", xmlNameSpace);
                xw.WriteAttributeString("partition", XmlConvert.ToString(part.Id));
                xw.WriteEndElement();
            }
            xw.WriteEndElement();
        }

        private void WriteIndividuals(XmlWriter xw, Story story)
        {
            foreach (IndividualsPool ip in story.Pools)
            {
                xw.WriteStartElement("xeml", "IndividualsPool", xmlNameSpace);
                xw.WriteAttributeString("NS", ip.NameSpace);
                xw.WriteAttributeString("GermPlasm", XmlLexicalType.ConvertToToken(ip.GermPlasm));
                WriteAnnotations(xw, ip);

                foreach (Individual ind in ip.Individuals)
                {
                    xw.WriteStartElement("xeml", "Individual", xmlNameSpace);
                    xw.WriteAttributeString("Id", XmlConvert.ToString(ind.Id));
                    WriteAnnotations(xw, ind);
                    xw.WriteEndElement();
                }
                xw.WriteEndElement();
            }
        }

        private void WriteEvent(XmlWriter xw, Event op)
        {
            xw.WriteStartElement("xeml", "Event", xmlNameSpace);

            xw.WriteAttributeString("TimePoint", op.TimePoint.ToString());
            xw.WriteAttributeString("Label", op.Label);
            WriteAnnotations(xw, op);
            xw.WriteEndElement();
        }

        private void WriteObservationPoint(XmlWriter xw, ObservationPoint op)
        {
            xw.WriteStartElement("xeml", "ObservationPoint", xmlNameSpace);
            xw.WriteAttributeString("Id", op.Id.ToString());
            xw.WriteAttributeString("TargetTime", op.TimePoint.ToString());
            WriteAnnotations(xw, op);
            foreach (Observation ob in op.Observations)
            {
                if (ob.Destructive)
                {
                    xw.WriteStartElement("xeml", "DestructiveObservation", xmlNameSpace);
                }
                else
                {
                    xw.WriteStartElement("xeml", "Observation", xmlNameSpace);
                    if (ob.IndividualInfluence)
                        xw.WriteAttributeString("IndividualInfluence", XmlConvert.ToString(ob.IndividualInfluence));
                }
                WriteAnnotations(xw, ob);

                if (ob.Individual != null)
                    xw.WriteAttributeString("individual", XmlConvert.ToString(ob.Individual.Id));
                if (ob.IndividualsPool != null)
                {
                    xw.WriteAttributeString("poolNS", ob.IndividualsPool.NameSpace);
                    xw.WriteAttributeString("poolGermPlasm", ob.IndividualsPool.GermPlasm);

                }
                xw.WriteAttributeString("Duration", ob.Duration.ToString());

                foreach (Partition part in ob.Partitions)
                {
                    xw.WriteStartElement("xeml", "Partition", xmlNameSpace);
                    xw.WriteAttributeString("Id", XmlConvert.ToString(part.Id));
                    WriteAnnotations(xw, part);
                    foreach (Term mat in part.Material)
                    {
                        xw.WriteStartElement("xeml", "Material", xmlNameSpace);

                        if (!String.IsNullOrEmpty(mat.NameSpaceAlias))
                            xw.WriteAttributeString("NS", mat.NameSpaceAlias);
                        else
                            xw.WriteAttributeString("NS", "none");
                        xw.WriteAttributeString("TermId", XmlLexicalType.ConvertToToken(mat.TermId));
                        xw.WriteAttributeString("term", XmlLexicalType.ConvertToToken(mat.Name));

                        WriteAnnotations(xw, mat);

                        xw.WriteEndElement();
                    }
                    foreach (Term pos in part.Positions)
                    {
                        xw.WriteStartElement("xeml", "Position", xmlNameSpace);

                        if (!String.IsNullOrEmpty(pos.NameSpaceAlias))
                            xw.WriteAttributeString("NS", pos.NameSpaceAlias);
                        else
                            xw.WriteAttributeString("NS", "none");
                        xw.WriteAttributeString("TermId", XmlLexicalType.ConvertToToken(pos.TermId));
                        xw.WriteAttributeString("term", XmlLexicalType.ConvertToToken(pos.Name));

                        WriteAnnotations(xw, pos);
                        foreach (Value val in ((VariableTerm)pos).Values)
                        {
                            xw.WriteStartElement("xeml", "Value", xmlNameSpace);
                            xw.WriteAttributeString("Context", val.Context);
                            if (!String.IsNullOrEmpty(val.Unit))
                                xw.WriteAttributeString("Unit", val.Unit);
                            if (!String.IsNullOrEmpty(val.Label))
                                xw.WriteAttributeString("Label", val.Label);
                            xw.WriteValue(val.Val);
                            xw.WriteEndElement();
                        }
                        xw.WriteEndElement();
                    }
                    xw.WriteEndElement();
                }

                foreach (Term item in ob.DevelopmentalStages)
                {
                    xw.WriteStartElement("xeml", "DevelopmentalStage", xmlNameSpace);

                    if (!String.IsNullOrEmpty(item.NameSpaceAlias))
                        xw.WriteAttributeString("NS", item.NameSpaceAlias);
                    else
                        xw.WriteAttributeString("NS", "none");
                    xw.WriteAttributeString("TermId", XmlLexicalType.ConvertToToken(item.TermId));
                    xw.WriteAttributeString("term", XmlLexicalType.ConvertToToken(item.Name));

                    WriteAnnotations(xw, item);

                    xw.WriteEndElement();
                }
                xw.WriteEndElement();
            }
            xw.WriteEndElement();
        }

        private void WriteParameter(XmlWriter xw, DynamicTerm p)
        {
            xw.WriteStartElement("xeml", "Variable", xmlNameSpace);
            xw.WriteAttributeString("NS", p.NameSpaceAlias);
            if (!String.IsNullOrEmpty(p.TermId))
                xw.WriteAttributeString("TermId", XmlLexicalType.ConvertToToken(p.TermId));
            //if (!String.IsNullOrEmpty(p.GroupPath))
            //    xw.WriteAttributeString("GroupPath", XmlLexicalType.ConvertToToken(p.GroupPath));
            xw.WriteAttributeString("Name", XmlLexicalType.ConvertToToken(p.Name));

            WriteAnnotations(xw, p);

            //     p.Values.Sort(delegate(ValueBase vb1, ValueBase vb2) { return vb1.Time.CompareTo(vb2.Time); });

            foreach (TimeSpan ts in p.Values.ListDistinctIdentifiers())
            {
                xw.WriteStartElement("xeml", "ValueSet", xmlNameSpace);
                xw.WriteAttributeString("TimePoint", ts.ToString());
                foreach (ValueBase vb in p.Values.FindAll(ts))
                {
                    WriteValues(xw, vb);
                }
                xw.WriteEndElement();
            }
            xw.WriteEndElement();
        }

        private void WriteValues(XmlWriter xw, ValueBase vb)
        {
            if (vb is DynamicValue)
            {
                DynamicValue v = vb as DynamicValue;

                xw.WriteStartElement("xeml", "Value", xmlNameSpace);
                if (!v.IsCycleValue)
                    xw.WriteAttributeString("Context", v.Context);
                if (!String.IsNullOrEmpty(v.Unit))
                    xw.WriteAttributeString("Unit", v.Unit);
                if (v.IsCycleValue)
                    xw.WriteAttributeString("Duration", v.Time.ToString());
                if (!String.IsNullOrEmpty(v.Label))
                    xw.WriteAttributeString("Label", v.Label);
                xw.WriteValue(v.Val);
                xw.WriteEndElement();
            }
            else if (vb is Cycle)
            {
                Cycle c = vb as Cycle;
                xw.WriteStartElement("xeml", "Cycle", xmlNameSpace);
                xw.WriteAttributeString("Context", c.Context);
                if (!String.IsNullOrEmpty(c.Unit))
                    xw.WriteAttributeString("Unit", c.Unit);
                if (!String.IsNullOrEmpty(c.Label))
                    xw.WriteAttributeString("Label", c.Label);
                foreach (ValueBase x in c.Values)
                {
                    WriteValues(xw, x);
                }

                xw.WriteEndElement();
            }
        }

        private void WriteAnnotations(XmlWriter xw, XemlAnnotatableObject xo)
        {
            foreach (TaggedAnnotation kvp in xo.Annotations)
            {
                if (!String.IsNullOrEmpty(kvp.Annotation))
                {
                    xw.WriteStartElement("Annotation", xmlNameSpace);
                    xw.WriteAttributeString("Tag", kvp.Tag);
                    xw.WriteValue(kvp.Annotation);
                    xw.WriteEndElement();
                }
            }
        }

        private void WriteSampleMapping(XmlWriter xw, int sampleId, string providerName)
        {
            xw.WriteStartElement("xeml", "SampleMapping", xmlNameSpace);
            xw.WriteAttributeString("sample", sampleId.ToString());
            foreach (SidMapping sid in _provDataMappings[providerName].FindAll(sampleId))
            {
                xw.WriteStartElement("xeml", "Sid", xmlNameSpace);
                xw.WriteAttributeString("Provider", XmlLexicalType.ConvertToToken(providerName));
                foreach (string key in sid.ForeignKeyMap.Keys)
                {
                    xw.WriteStartElement("xeml", "Subkey", xmlNameSpace);
                    xw.WriteAttributeString("Name", key);
                    xw.WriteValue(sid.ForeignKeyMap[key]);
                    xw.WriteEndElement();
                }
                xw.WriteEndElement();
            }
            xw.WriteEndElement();
        }
        #endregion

        #region Helper methods
        public TimeSpan TryTimespanSet(string tstring, object relatedObject)
        {
            if (TimeSpanExtension.Evaluate(tstring, TimeSpanParseOption.DoubleIsDay) == TimeSpanSyntax.Invalid)
            {
                OnValidation(SeverityCode.Error, "Invalid time format. '" + tstring + "'", relatedObject, true);
            }
            return TimeSpanExtension.Parse(tstring);
        }
        #endregion

        #region IDocument Events
        public event EventHandler Loaded;
        public event EventHandler<ValidationMessage> Validation;

        protected virtual void OnLoaded()
        {
            if (Loaded != null)
            {
                Loaded(this, new EventArgs());
            }
        }

        public event EventHandler Changed;
        public virtual void OnChanged()
        {
            if (Changed != null)
            {
                Changed(this, new EventArgs());
            }
        }

        protected virtual void OnValidation(ValidationMessage vm)
        {
            if (Validation != null)
                Validation(this, vm);
        }

        private void OnValidation(SeverityCode severityCode, string message, object relatedObject, bool inhibtRendering)
        {
            ValidationMessage vm = new ValidationMessage(severityCode, message, relatedObject, inhibtRendering);
            OnValidation(vm);
        }

        #endregion

        #region Eventhandler
        void OnStoryBoardChanged(object sender, EventArgs e)
        {
            OnChanged();
        }

        private void OnXmlValidationEventHandler(object sender, ValidationEventArgs e)
        {
            switch (e.Severity)
            {
                case XmlSeverityType.Error:
                    {
                        XmlValidationMessage vm = new XmlValidationMessage(SeverityCode.Error, e.Message, this, false, e.Exception.LineNumber, e.Exception.LinePosition);
                        OnValidation(vm);
                        Debug.WriteLine("Schema Validation Error: {0}", e.Message);
                        //this.lastValidationError = e.Message;
                    }
                    break;
                case XmlSeverityType.Warning:
                    {
                        XmlValidationMessage vm = new XmlValidationMessage(SeverityCode.Warning, e.Message, this, false, e.Exception.LineNumber, e.Exception.LinePosition);
                        OnValidation(vm);
                        Debug.WriteLine("Schema Validation Warning: {0}", e.Message);

                    }
                    break;
            }
        }

        private void SchemaValidationHandler(object sender, ValidationEventArgs e)
        {
            isSchemaConform = false;

            switch (e.Severity)
            {
                case XmlSeverityType.Error:
                    {
                        ValidationMessage vm = new ValidationMessage(SeverityCode.Error, e.Message, null, false);
                        OnValidation(vm);
                        Debug.WriteLine("Schema Validation Error: {0}", e.Message);
                        //this.lastValidationError = e.Message;
                    }
                    break;
                case XmlSeverityType.Warning:
                    {
                        ValidationMessage vm = new ValidationMessage(SeverityCode.Warning, e.Message, null, false);
                        OnValidation(vm);
                        Debug.WriteLine("Schema Validation Warning: {0}", e.Message);

                    }
                    break;
            }
        }
        #endregion

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
        #endregion
    }
}
